#include "Renderer.h"
#include "Window.h"
#include "Buffers.h"
#include "VertexBuffers.h"
#include "Device.h"
#include "Texture.h"
#include "Pipeline.h"

#include <iostream>

namespace vkengine
{
	static void FramebufferResizeCallback(GLFWwindow* window, int width, int height) 
	{
		auto renderer = reinterpret_cast<Renderer*>(glfwGetWindowUserPointer(window));
		renderer->m_FramebufferResized = true;
	}

	void Renderer::Init(GLFWwindow* window)
	{
		glfwSetWindowUserPointer(window, this);
		glfwSetFramebufferSizeCallback(window, FramebufferResizeCallback);
	}
	void Renderer::CreateSyncObjects(VkDevice device) 
	{
		m_ImageAvailableSemaphore.resize(MAX_FRAMES_IN_FLIGHT);
		m_RenderFinishedSemaphore.resize(MAX_FRAMES_IN_FLIGHT);
		m_InFlightFence.resize(MAX_FRAMES_IN_FLIGHT);
		VkSemaphoreCreateInfo semaphoreInfo{};
		semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;

		VkFenceCreateInfo fenceInfo{};
		fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
		fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;

		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++)
		{
			if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &m_ImageAvailableSemaphore[i]) != VK_SUCCESS ||
				vkCreateSemaphore(device, &semaphoreInfo, nullptr, &m_RenderFinishedSemaphore[i]) != VK_SUCCESS ||
				vkCreateFence(device, &fenceInfo, nullptr, &m_InFlightFence[i]) != VK_SUCCESS) {
				throw std::runtime_error("failed to create synchronization objects for a frame!");
			}
		}


	}

	void Renderer::DrawFrame(Window window,Device* device,Pipeline* pipeline,Buffers* buffers,Texture* texture)
	{
		vkWaitForFences(device->GetDevice(), 1, &m_InFlightFence[m_CurrentFrame], VK_TRUE, UINT64_MAX);
		

		uint32_t imageIndex;
		VkResult result = vkAcquireNextImageKHR(device->GetDevice(), device->GetSwapChain(), UINT64_MAX, m_ImageAvailableSemaphore[m_CurrentFrame], VK_NULL_HANDLE, &imageIndex);

		if (result == VK_ERROR_OUT_OF_DATE_KHR) {
			device->RecreateSwapChain(window, buffers, pipeline, texture);
			return;
		}
		else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
			throw std::runtime_error("failed to acquire swap chain image!");
		}
		
		buffers->UpdateUniformBuffer(device, imageIndex);
		vkResetFences(device->GetDevice(), 1, &m_InFlightFence[m_CurrentFrame]);

		vkResetCommandBuffer(buffers->GetCommandBuffers()[m_CurrentFrame], /*VkCommandBufferResetFlagBits*/ 0);
		buffers->RecordCommandBuffer(buffers->GetCommandBuffers()[m_CurrentFrame],imageIndex,m_CurrentFrame,device,pipeline);

		VkSubmitInfo submitInfo{};
		submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
		submitInfo.commandBufferCount = 1;
		submitInfo.pCommandBuffers = &buffers->GetCommandBuffersRef()[m_CurrentFrame];

		VkSemaphore waitSemaphores[] = { m_ImageAvailableSemaphore[m_CurrentFrame]};
		VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
		submitInfo.waitSemaphoreCount = 1;
		submitInfo.pWaitSemaphores = waitSemaphores;
		submitInfo.pWaitDstStageMask = waitStages;


		VkSemaphore signalSemaphores[] = { m_RenderFinishedSemaphore[m_CurrentFrame]};
		submitInfo.signalSemaphoreCount = 1;
		submitInfo.pSignalSemaphores = signalSemaphores;

		if (vkQueueSubmit(device->GraphicsQueue(), 1, &submitInfo, m_InFlightFence[m_CurrentFrame]) != VK_SUCCESS) {
			throw std::runtime_error("failed to submit draw command buffer!");
		}

		VkPresentInfoKHR presentInfo{};
		presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;

		presentInfo.waitSemaphoreCount = 1;
		presentInfo.pWaitSemaphores = signalSemaphores;

		VkSwapchainKHR swapChains[] = { device->GetSwapChain()};
		presentInfo.swapchainCount = 1;
		presentInfo.pSwapchains = swapChains;

		presentInfo.pImageIndices = &imageIndex;

		result = vkQueuePresentKHR(device->PresentQueue(), &presentInfo);

		if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || m_FramebufferResized) {
			m_FramebufferResized = false;
			device->RecreateSwapChain(window,buffers,pipeline, texture);
		}
		else if (result != VK_SUCCESS) {
			throw std::runtime_error("failed to present swap chain image!");
		}

		m_CurrentFrame = (m_CurrentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
	}

	void Renderer::Cleanup(Device* device)
	{
		for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) 
		{
			vkDestroySemaphore(device->GetDevice(), m_RenderFinishedSemaphore[i], nullptr);
			vkDestroySemaphore(device->GetDevice(), m_ImageAvailableSemaphore[i], nullptr);
			vkDestroyFence(device->GetDevice(), m_InFlightFence[i], nullptr);
		}
	}

	void Renderer::DeviceWaitIdle(Device* device)
	{
		vkDeviceWaitIdle(device->GetDevice());
	}
}